package es.chaveweb.poZit.client.ui.cell;

import com.google.gwt.cell.client.AbstractSafeHtmlCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.ImportedWithPrefix;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesBuilder;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.TextArea;
import com.grupit.engine.util.Format;
import com.grupit.engine.util.FormatterLogger;

import es.chaveweb.poZit.client.mvp.widget.PoZitData;
import es.chaveweb.poZit.client.ui.Appearance;
import es.chaveweb.poZit.client.ui.cell.PoZitCell.PoZitAppearance.PoZitCss;
import es.chaveweb.poZit.client.ui.cell.PoZitCell.PoZitAppearance.PoZitResources;

public abstract class PoZitCell extends AbstractSafeHtmlCell<PoZitData> {
	private static FormatterLogger logger = FormatterLogger.getLogger(PoZitCell.class);

	public enum Color {
		YELLOW, GREEN
	}

	public interface Template extends SafeHtmlTemplates {
		@Template("<div class='{0}' style='{1}'><img src='{2}' class='{3}'/><div class='{4}'></div><textarea class='{5}' style='{6}' spellcheck='false'>{7}</textarea></div>")
		SafeHtml poZit(String containerStyleClass, SafeStyles containerExplicitStyle, SafeUri pinImageUri,
				String pinImageStyleClass, String cornerBoxStyleClass, String contentStyleClass,
				SafeStyles contentExplicitStyle, String poZitContent);

		public enum PoZitElementIndex {
			IMG(0), CORNER(1), CONTENT(2);

			private int index;

			private PoZitElementIndex(int index) {
				this.index = index;
			}

			public int getIndex() {
				return index;
			}
		}

	}

	private static Template template = GWT.create(Template.class);

	public static class PoZitAppearance extends Appearance<PoZitResources> {
		public PoZitAppearance(PoZitResources resources) {
			super(resources);
		}

		public static interface PoZitResources extends ClientBundle {
			@Source({ PoZitCss.DEFAULT_COMMON_CSS, PoZitCss.DEFAULT_GREEN_CSS })
			PoZitCss green();

			@Source({ PoZitCss.DEFAULT_COMMON_CSS, PoZitCss.DEFAULT_YELLOW_CSS })
			PoZitCss yellow();

			ImageResource pin();
		}

		@ImportedWithPrefix("poZit")
		public interface PoZitCss extends CssResource {
			String DEFAULT_COMMON_CSS = "es/chaveweb/poZit/client/ui/cell/common.css";
			String DEFAULT_GREEN_CSS = "es/chaveweb/poZit/client/ui/cell/green.css";
			String DEFAULT_YELLOW_CSS = "es/chaveweb/poZit/client/ui/cell/yellow.css";

			String container();

			String corner();

			String pin();

			String content();

			String focused();
		}

		@Override
		public void ensureCssResourcesInjected() {
			this.resources.green().ensureInjected();
			this.resources.yellow().ensureInjected();
		}
	}

	private static class PoZitCellRenderer implements SafeHtmlRenderer<PoZitData> {

		private PoZitResources theme;
		private PoZitCss css;

		public PoZitCellRenderer(PoZitAppearance poZitAppearance, Color color) {
			this.theme = poZitAppearance.getResources();
			setColor(color);
		}

		public void setColor(Color color) {
			switch (color) {
			case GREEN:
				this.css = this.theme.green();
				break;
			case YELLOW:
			default:
				this.css = this.theme.yellow();
			}
		}

		@Override
		public SafeHtml render(PoZitData note) {
			return getPoZit(note);
		}

		protected SafeHtml getPoZit(PoZitData note) {

			String noteText = (note != null && note.getNoteText() != null) ? note.getNoteText() : "";
			SafeStylesBuilder containerExplicitStyle = new SafeStylesBuilder();
			containerExplicitStyle.appendTrustedString((note != null) ? Format.substitute(
					"position: absolute; left: {0}px; top:{1}px;", note.getX(), note.getY()) : "");
			SafeStylesBuilder contentExplicitStyle = new SafeStylesBuilder();

			return template.poZit(css.container(), containerExplicitStyle.toSafeStyles(),
					this.theme.pin().getSafeUri(), css.pin(), css.corner(), css.content(),
					contentExplicitStyle.toSafeStyles(), noteText);
		}

		@Override
		public void render(PoZitData note, SafeHtmlBuilder builder) {
			builder.append(getPoZit(note));
		}

		public PoZitCss getCss() {
			return css;
		}
	}

	private FieldUpdater<PoZitData, String> textContentUpdater;
	private int maxX = 0;
	private int maxY = 0;

	public PoZitCell(FieldUpdater<PoZitData, String> textContentUpdater) {
		this(Color.YELLOW, textContentUpdater);
	}

	public PoZitCell(Color color, FieldUpdater<PoZitData, String> textContentUpdater) {
		super(new PoZitCellRenderer(new PoZitAppearance(GWT.<PoZitResources> create(PoZitResources.class)), color),
				"mousedown", "mouseup", "mouseout", "mouseover", "mousemove", "mousewheel", "change", "focus", "blur",
				"keypress");
		this.textContentUpdater = textContentUpdater;
	}

	@Override
	protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
		if (data != null) {
			sb.append(data);
		}
	}

	@Override
	public void render(com.google.gwt.cell.client.Cell.Context context, PoZitData data, SafeHtmlBuilder sb) {
		if (context.getIndex() == 0) {
			if (data != null) {
				this.setMaxX(data.getX());
				this.setMaxY(data.getY());
			} else {
				this.setMaxX(0);
				this.setMaxY(0);
			}
		} else {
			if (data != null && data.getX() > this.getMaxX()) {
				this.setMaxX(data.getX());
			}

			if (data != null && data.getY() > this.getMaxY()) {
				this.setMaxY(data.getY());
			}
		}

		render(context, getRenderer().render(data), sb);
	}

	public void setColor(Color color) {
		((PoZitCellRenderer) this.getRenderer()).setColor(color);
	}

	@Override
	public boolean isEditing(com.google.gwt.cell.client.Cell.Context context, Element parent, PoZitData value) {
		return true;
	}

	@Override
	public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context, Element parent, PoZitData value,
			NativeEvent event, ValueUpdater<PoZitData> valueUpdater) {
		super.onBrowserEvent(context, parent, value, event, valueUpdater);

		String eventType = event.getType();

		if ("change".equals(eventType)) {
			onChange(parent, value, event, valueUpdater);
		}

		if ("focus".equals(eventType)) {
			onFocus(parent, event);
		}

		if ("blur".equals(eventType)) {
			onBlur(parent, event);
		}

		if ("mousedown".equals(eventType)) {
			this.onMouseDown(context, parent, value, event, valueUpdater);
		}

		if ("mouseup".equals(eventType)) {
			this.onMouseUp(context, parent, value, event, valueUpdater);
		}

		if ("mousemove".equals(eventType)) {
			this.onMouseMove(context, parent, value, event, valueUpdater);
		}

		if ("keypress".equals(eventType)) {
			this.onKeyPress(context, parent, value, event, valueUpdater);
		}
	}

	protected void onKeyPress(com.google.gwt.cell.client.Cell.Context context, Element parent, PoZitData value,
			NativeEvent event, ValueUpdater<PoZitData> valueUpdater) {
		Element containerElm = parent.getFirstChildElement();
		Element contentElm = Element.as(containerElm.getChild(Template.PoZitElementIndex.CONTENT.index));
		adjustContentSize(contentElm);
	}

	protected void onChange(Element parent, PoZitData value, NativeEvent event, ValueUpdater<PoZitData> valueUpdater) {
		if (Element.is(event.getEventTarget())) {
			Element elm = Element.as(event.getEventTarget());
			TextArea textArea = new TextArea(elm) {

			};

			if (this.textContentUpdater != null) {
				this.textContentUpdater.update(0, value, textArea.getText());
			}

			if (valueUpdater != null) {
				valueUpdater.update(value);
			}
		}
	}

	protected void onFocus(Element parent, NativeEvent event) {
		Element containerElm = parent.getFirstChildElement();

		String focusedStyle = ((PoZitCellRenderer) this.getRenderer()).getCss().focused();
		containerElm.addClassName(focusedStyle);

		Element contentElm = Element.as(containerElm.getChild(Template.PoZitElementIndex.CONTENT.index));
		adjustContentSizeRepeating(contentElm);
	}

	protected void onBlur(Element parent, NativeEvent event) {
		Element containerElm = parent.getFirstChildElement();
		Element contentElm = Element.as(containerElm.getChild(Template.PoZitElementIndex.CONTENT.index));

		String focusedStyle = ((PoZitCellRenderer) this.getRenderer()).getCss().focused();
		containerElm.removeClassName(focusedStyle);
		contentElm.getStyle().clearWidth();
		contentElm.getStyle().clearHeight();
	}

	protected void adjustContentSizeRepeating(final Element textContentElm) {
		Timer t = new Timer() {

			@Override
			public void run() {
				boolean adjusted = adjustContentSize(textContentElm);

				if (!adjusted) {
					this.cancel();
				}
			}

		};
		t.scheduleRepeating(1000);
		t.run();
	}

	protected boolean adjustContentSize(Element textContentElm) {
		boolean adjusted = false;
		int clientHeight = textContentElm.getClientHeight();
		int scrollHeight = textContentElm.getScrollHeight();

		if (scrollHeight > clientHeight) {
			double add = (scrollHeight - clientHeight) / 3;

			double width = textContentElm.getOffsetWidth() + (int) add;
			double height = textContentElm.getOffsetHeight() + (int) add;

			textContentElm.getStyle().setWidth(width, Unit.PX);
			textContentElm.getStyle().setHeight(height, Unit.PX);
			adjusted = true;
		}
		return adjusted;
	}

	public abstract void onMouseDown(com.google.gwt.cell.client.Cell.Context context, Element parent, PoZitData value,
			NativeEvent event, ValueUpdater<PoZitData> valueUpdater);

	public abstract void onMouseUp(com.google.gwt.cell.client.Cell.Context context, Element parent, PoZitData value,
			NativeEvent event, ValueUpdater<PoZitData> valueUpdater);

	public abstract void onMouseMove(com.google.gwt.cell.client.Cell.Context context, Element parent, PoZitData value,
			NativeEvent event, ValueUpdater<PoZitData> valueUpdater);

	public int getMaxX() {
		return maxX;
	}

	public int getMaxY() {
		return maxY;
	}

	public void setMaxX(int maxX) {
		this.maxX = maxX;
	}

	public void setMaxY(int maxY) {
		this.maxY = maxY;
	}

}
